#include "panorama_file_server.h"
#include <regex>
#include "json-builder.h"

extern "C" {
#include "http_util.h"
#include "file_util.h"
}

PanoramaFileServer::PanoramaFileServer(PanoFileServerOption& option) :FileServer(option) {

}

void PanoramaFileServer::QueryServerFiles(evhttp_request* req)
{
	enum evhttp_cmd_type cmd_type = evhttp_request_get_command(req);
	if (cmd_type != EVHTTP_REQ_GET) {
		cout << "request type is not Get" << endl;
		return_http_error(req, "request type is not Get");
		return;
	}

	const char* uri = evhttp_request_get_uri(req);
	struct evhttp_uri* evuri = evhttp_uri_parse(uri);
	const char* query = evhttp_uri_get_query(evuri);

	struct evkeyvalq params;
	evhttp_parse_query_str(query, &params);
	const char* start_str = evhttp_find_header(&params, "start");
	const char* limit_str = evhttp_find_header(&params, "limit");
	const char* path_str = evhttp_find_header(&params, "path");

	unsigned int start = 0;
	unsigned int limit = 20;
	fs::path dir_path = _option->base_dirctory;

	if (start_str) {
		start = atoi(start_str);
	}
	if (limit_str && atoi(limit_str) > 0) {
		limit = atoi(limit_str);
	}
	if (_option->enable_directory_search && path_str) {
		dir_path = dir_path / path_str;
	}

	cout << "start is " << start << endl;
	cout << "limit is " << limit << endl;
	cout << "search dir path is " << dir_path << endl;

	vector<PanoramaInfo> panoinfos;
	SearchPanoramaFiles(dir_path, start, limit, panoinfos);

	struct evbuffer* retbuff = evbuffer_new();
	struct evkeyvalq* reqheader = evhttp_request_get_output_headers(req);
	evhttp_add_header(reqheader, "Content-Type", "application/json");
	evhttp_add_header(reqheader, "charset", "UTF-8");

	json_value* top_jv = json_object_new(0);
	json_object_push(top_jv, "status", json_integer_new(200));

	json_value* data_arr_jv = json_array_new(panoinfos.size());
	for (size_t i = 0; i < panoinfos.size(); i++)
	{
		json_value* data_obj_jv = json_object_new(0);
		json_object_push(data_obj_jv, "pano_name", json_string_new(panoinfos[i].pano_name.c_str()));
		json_object_push(data_obj_jv, "rows", json_integer_new(panoinfos[i].rows));
		json_object_push(data_obj_jv, "cols", json_integer_new(panoinfos[i].cols));

		json_value* imgsize_obj_jv = json_object_new(0);
		json_object_push(imgsize_obj_jv, "width", json_integer_new(panoinfos[i].img_width));
		json_object_push(imgsize_obj_jv, "height", json_integer_new(panoinfos[i].img_height));
		json_object_push(data_obj_jv, "img_size", imgsize_obj_jv);

		string file_url;
		if (path_str) {
			file_url = _option->base_url + "/" + path_str + "/" + panoinfos[i].pano_name;
		}
		else {
			file_url = _option->base_url + "/" + panoinfos[i].pano_name;
		}
		json_object_push(data_obj_jv, "url", json_string_new(file_url.c_str()));
		
		json_object_push(data_obj_jv, "last_write_time", json_integer_new(panoinfos[i].last_write_time));

		json_array_push(data_arr_jv, data_obj_jv);
	}
	json_object_push(top_jv, "data", data_arr_jv);

	json_object_push(top_jv, "start", json_integer_new(start));
	json_object_push(top_jv, "length", json_integer_new(panoinfos.size()));

	size_t json_strlen = json_measure(top_jv);
	char json_str[json_strlen];
	json_serialize(json_str, top_jv);

	json_value_free(top_jv);

#ifdef _WIN32
	size_t utf8_str_len = 100000;
	char utf8_str[utf8_str_len];
	gbk_to_utf8(json_str, utf8_str, utf8_str_len);

	evbuffer_add_printf(retbuff, utf8_str);
	evhttp_send_reply(req, HTTP_OK, "OK", retbuff);
	evbuffer_free(retbuff);
#else
	evbuffer_add_printf(retbuff, json_str);
	evhttp_send_reply(req, HTTP_OK, "OK", retbuff);
	evbuffer_free(retbuff);
#endif
}

void PanoramaFileServer::QueryServerDirs(evhttp_request* req)
{
	enum evhttp_cmd_type cmd_type = evhttp_request_get_command(req);
	if (cmd_type != EVHTTP_REQ_GET) {
		cout << "request type is not Get" << endl;
		return_http_error(req, "request type is not Get");
		return;
	}

	const char* uri = evhttp_request_get_uri(req);
	struct evhttp_uri* evuri = evhttp_uri_parse(uri);
	const char* query = evhttp_uri_get_query(evuri);

	struct evkeyvalq params;
	evhttp_parse_query_str(query, &params);
	const char* start_str = evhttp_find_header(&params, "start");
	const char* limit_str = evhttp_find_header(&params, "limit");
	const char* path_str = evhttp_find_header(&params, "path");

	unsigned int start = 0;
	unsigned int limit = 20;
	fs::path dir_path = _option->base_dirctory;

	if (start_str) {
		start = atoi(start_str);
	}
	if (limit_str && atoi(limit_str) > 0) {
		limit = atoi(limit_str);
	}
	if (_option->enable_directory_search && path_str) {
		dir_path = dir_path / path_str;
	}

	vector<DirInfo> dirinfos;
	SearchPanoramaDirs(dir_path, start, limit, dirinfos);

	struct evbuffer* retbuff = evbuffer_new();
	struct evkeyvalq* reqheader = evhttp_request_get_output_headers(req);
	evhttp_add_header(reqheader, "Content-Type", "application/json");
	evhttp_add_header(reqheader, "charset", "UTF-8");

	json_value* top_jv = json_object_new(0);
	json_object_push(top_jv, "status", json_integer_new(200));

	json_value* data_arr_jv = json_array_new(dirinfos.size());
	for (size_t i = 0; i < dirinfos.size(); i++)
	{
		json_value* data_obj_jv = json_object_new(0);
		json_object_push(data_obj_jv, "dir_name", json_string_new(dirinfos[i].dir_name.c_str()));

		json_array_push(data_arr_jv, data_obj_jv);
	}
	json_object_push(top_jv, "data", data_arr_jv);

	json_object_push(top_jv, "start", json_integer_new(start));
	json_object_push(top_jv, "length", json_integer_new(dirinfos.size()));

	size_t json_strlen = json_measure(top_jv);
	char json_str[json_strlen];
	json_serialize(json_str, top_jv);

	json_value_free(top_jv);

#ifdef _WIN32
	size_t utf8_str_len = 100000;
	char utf8_str[utf8_str_len];
	gbk_to_utf8(json_str, utf8_str, utf8_str_len);

	evbuffer_add_printf(retbuff, utf8_str);
	evhttp_send_reply(req, HTTP_OK, "OK", retbuff);
	evbuffer_free(retbuff);
#else
	evbuffer_add_printf(retbuff, json_str);
	evhttp_send_reply(req, HTTP_OK, "OK", retbuff);
	evbuffer_free(retbuff);
#endif
}

void PanoramaFileServer::RenameServerFile(evhttp_request* req)
{
	enum evhttp_cmd_type cmd_type = evhttp_request_get_command(req);
	if (cmd_type != EVHTTP_REQ_POST) {
		cout << "request type is not Post" << endl;
		return_http_error(req, "request type is not Post");
		return;
	}

	const char* uri = evhttp_request_get_uri(req);
	struct evhttp_uri* evuri = evhttp_uri_parse(uri);
	const char* query = evhttp_uri_get_query(evuri);

	struct evkeyvalq params;
	evhttp_parse_query_str(query, &params);
	const char* filename = evhttp_find_header(&params, "filename");
	const char* new_filename = evhttp_find_header(&params, "new_filename");

	if (filename == 0 || new_filename == 0) {
		cout << "parameter error" << endl;
		return_http_error(req, "parameter error");
	}

	fs::path file_path = fs::path(_option->base_dirctory) / filename;
	fs::path new_file_path = fs::path(_option->base_dirctory) / new_filename;

	if (!fs::exists(file_path)) {
		cout << "source file is not exist" << endl;
		return_http_error(req, "source file is not exist");
		return;
	}
	if (fs::exists(new_file_path)) {
		cout << "target file is exist" << endl;
		return_http_error(req, "target file is exist");
		return;
	}

	PanoramaInfo info;
	if (!CheckPanoramaFile(file_path, &info)) {
		cout << "target file is panorama" << endl;
		return_http_error(req, "target file is panorama");
		return;
	}

	fs::rename(file_path, new_file_path);

	struct evbuffer* retbuff = evbuffer_new();
	struct evkeyvalq* reqheader = evhttp_request_get_output_headers(req);
	evhttp_add_header(reqheader, "Content-Type", "application/json");
	evhttp_add_header(reqheader, "charset", "UTF-8");

	json_value* top_jv = json_object_new(0);
	json_object_push(top_jv, "status", json_integer_new(200));

	size_t json_strlen = json_measure(top_jv);
	char json_str[json_strlen];
	json_serialize(json_str, top_jv);

	json_value_free(top_jv);
#ifdef _WIN32
	size_t utf8_str_len = 100000;
	char utf8_str[utf8_str_len];
	gbk_to_utf8(json_str, utf8_str, utf8_str_len);

	evbuffer_add_printf(retbuff, utf8_str);
	evhttp_send_reply(req, HTTP_OK, "OK", retbuff);
	evbuffer_free(retbuff);
#else
	evbuffer_add_printf(retbuff, json_str);
	evhttp_send_reply(req, HTTP_OK, "OK", retbuff);
	evbuffer_free(retbuff);
#endif
}

void PanoramaFileServer::DeleteServerFile(evhttp_request* req)
{
	enum evhttp_cmd_type cmd_type = evhttp_request_get_command(req);
	if (cmd_type != EVHTTP_REQ_POST) {
		cout << "request type is not Post" << endl;
		return_http_error(req, "request type is not Post");
		return;
	}

	const char* uri = evhttp_request_get_uri(req);
	struct evhttp_uri* evuri = evhttp_uri_parse(uri);
	const char* query = evhttp_uri_get_query(evuri);

	struct evkeyvalq params;
	evhttp_parse_query_str(query, &params);
	const char* filename = evhttp_find_header(&params, "filename");

	fs::path file_path = fs::path(_option->base_dirctory) / filename;
	if (!fs::exists(file_path)) {
		cout << "source file is not exist" << endl;
		return_http_error(req, "source file is not exist");
		return;
	}

	if (!fs::remove(file_path)) {
		cout << "file delete failed" << endl;
	}

	struct evbuffer* retbuff = evbuffer_new();
	struct evkeyvalq* reqheader = evhttp_request_get_output_headers(req);
	evhttp_add_header(reqheader, "Content-Type", "application/json");
	evhttp_add_header(reqheader, "charset", "UTF-8");

	json_value* top_jv = json_object_new(0);
	json_object_push(top_jv, "status", json_integer_new(200));

	size_t json_strlen = json_measure(top_jv);
	char json_str[json_strlen];
	json_serialize(json_str, top_jv);

	json_value_free(top_jv);

#ifdef _WIN32
	size_t utf8_str_len = 100000;
	char utf8_str[utf8_str_len];
	gbk_to_utf8(json_str, utf8_str, utf8_str_len);

	evbuffer_add_printf(retbuff, utf8_str);
	evhttp_send_reply(req, HTTP_OK, "OK", retbuff);
	evbuffer_free(retbuff);
#else
	evbuffer_add_printf(retbuff, json_str);
	evhttp_send_reply(req, HTTP_OK, "OK", retbuff);
	evbuffer_free(retbuff);
#endif
}

int PanoramaFileServer::SearchPanoramaFiles(fs::path dir_path, unsigned int start, unsigned int limit, vector<PanoramaInfo>& pano_infos)
{
	if (!fs::is_directory(dir_path)) {
		return -1;
	};
	pano_infos.clear();

	vector<fs::directory_entry> dentrys;
    for (auto& iter : fs::directory_iterator(dir_path)) {
        if (iter.is_directory()){
            dentrys.emplace_back(iter);
        }
    }
    sort(dentrys.begin(),dentrys.end(),[](fs::directory_entry a,fs::directory_entry b){
        return to_time_t(a.last_write_time()) > to_time_t(b.last_write_time());
    });

	unsigned int dir_index = 0;
	for (auto& iter : dentrys) {
		if (pano_infos.size() >= limit) {
			break;
		}
		PanoramaInfo info;
		if (CheckPanoramaFile(iter.path(), &info)) {
			pano_infos.emplace_back(info);
		}
	}
	return 0;
}

int PanoramaFileServer::SearchPanoramaDirs(fs::path dir_path, unsigned int start, unsigned int limit, vector<DirInfo>& dir_infos)
{
	if (!fs::is_directory(dir_path)) {
		return -1;
	};
	dir_infos.clear();

	vector<fs::directory_entry> dentrys;
    for (auto& iter : fs::directory_iterator(dir_path)) {
        if (iter.is_directory()){
            dentrys.emplace_back(iter);
        }
    }
    sort(dentrys.begin(),dentrys.end(),[](fs::directory_entry a,fs::directory_entry b){
        return to_time_t(a.last_write_time()) > to_time_t(b.last_write_time());
    });

	unsigned int dir_index = 0;
	for (auto& iter : dentrys) {
		if (dir_infos.size() >= limit) {
			break;
		}

		PanoramaInfo info;
		if (!CheckPanoramaFile(iter.path(), &info)) {
			if (dir_index >= start && dir_index < start + limit) {
				DirInfo di;
				di.dir_full_path = iter.path().string();
				di.dir_name = iter.path().filename().string();
				dir_infos.emplace_back(di);
			}
			dir_index++;
		}
	}
	return 0;
}

bool PanoramaFileServer::CheckPanoramaFile(fs::path file_path, PanoramaInfo* info)
{
	if (!fs::is_directory(file_path)) {
		return false;
	}

	bool is_pano = false;
	int rows = -1, cols = -1;
	int img_width = 0, img_height = 0;
	for (auto& iter : fs::directory_iterator(file_path)) {
		if (iter.is_regular_file()) {
			string fname = iter.path().filename().string();
			std::smatch matches;
			//bool search_res = std::regex_search(fname, matches, regex("^(\d+)_(\d+)_(\d+)_(\d+).jpg$"));
			bool search_res = std::regex_match(fname, matches, regex("^(\\d+)_(\\d+)_(\\d+)_(\\d+).jpg$"));
			if (matches.size() == 5) {
				int row = stoi(matches[1].str());
				int col = stoi(matches[2].str());
				int width = stoi(matches[3].str());
				int height = stoi(matches[4].str());
				
				rows = max(rows, row);
				cols = max(cols, col);
				
				if (img_width == 0) {
					img_width = width;
				}
				else if (img_width != width) {
					cout << "img_width not same" << endl;
					return false;
				}

				if (img_height == 0) {
					img_height = height;
				}
				else if (img_height != height) {
					cout << "img_height not same" << endl;
					return false;
				}
			}
		}
	}
	if (rows == -1 || cols == -1) {
		return false;
	}

	for (int row = 0; row < rows; row++)
	{
		for (int col = 0; col < cols; col++)
		{
			fs::path fpath = file_path / (to_string(row) + "_" + to_string(col) + "_" + to_string(img_width) + "_" + to_string(img_height) + ".jpg");
			if (!fs::is_regular_file(fpath)) {
				return false;
			}
		}
	}
	
	fs::path thum_path = file_path / "preview.jpg";
	if (!fs::is_regular_file(thum_path)) {
		cout << "thumail is not exist" << endl;
		return false;
	}

	info->rows = rows;
	info->cols = cols;
	info->pano_name = file_path.filename().string();
	info->pano_full_path = file_path.string();
	info->last_write_time = to_time_t(fs::last_write_time(file_path));
	info->img_width = img_width;
	info->img_height = img_height;
	return true;
}
